package top.hcode.hoj.tyedu;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.nutz.lang.Strings;
import org.nutz.lang.util.NutMap;
import org.springframework.stereotype.Component;
import top.hcode.hoj.common.LazyDao;
import top.hcode.hoj.pojo.entity.org.Org;
import top.hcode.hoj.pojo.entity.org.OrgUser;
import top.hcode.hoj.pojo.entity.org.TyOrg;
import top.hcode.hoj.pojo.entity.org.TyUser;
import top.hcode.hoj.tyedu.dto.TyClassMemberDTO;
import top.hcode.hoj.tyedu.dto.TyOrgMemberDTO;
import top.hcode.hoj.utils.CommonMethods;
import top.hcode.hoj.utils.SqlUtil;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 同步学校的学生和教师信息
 *
 * @author orangej
 * @since 2025/1/23
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class SyncSchoolUserService {
    private final LazyDao lazyDao;
    private final TyGatewayClient client;

    public void syncSchoolUser(String tySchoolId) {
        List<TyOrgMemberDTO> tyOrgMemberList = client.getAllOrgMetemberList(tySchoolId);
        saveTyOrgUser(tySchoolId, tyOrgMemberList);
        updateUserGenderAndAvatar(tySchoolId);
        queryUserAccount(tySchoolId);
//        updateTeacherMobile(tySchoolId);
        saveOrgUser(tySchoolId);
    }

    public void saveTyOrgUser(String tyOrgId, List<TyOrgMemberDTO> memberList) {
        // convert to NutMap
        Function<TyOrgMemberDTO, NutMap> convert = member -> NutMap.NEW()
                .addv("userId", member.getUserId())
                .addv("userType", member.getUserType())
                .addv("name", member.getName())
                .addv("classId", StrUtil.blankToDefault(StrUtil.join(",", member.getClassList()), null))
                .addv("tyOrgId", tyOrgId);

        // 清理历史数据
        lazyDao.runSql("update ty_user set ty_org_id = null where ty_org_id = ?", tyOrgId);

        String updateSql = "update ty_user set user_type = :userType, uname=:name, ty_class_id=:classId, ty_org_id=:tyOrgId where ty_user_id=:userId";
        String insertSql = "insert into ty_user (ty_org_id, ty_class_id, ty_user_id, user_type, uname, enabled)" +
                "values (:tyOrgId, :classId, :userId, :userType, :name, 1)";

        // 查询已存在的用户
        List<String> userIdList = memberList.stream().map(TyOrgMemberDTO::getUserId).collect(Collectors.toList());
        List<String> existUseridList = new ArrayList<>();
        ListUtil.split(userIdList, 1000)
                .parallelStream()
                .forEach(idList -> {
                    existUseridList.addAll(lazyDao.selectList(String.class, "select ty_user_id from ty_user where ty_user_id in (?)", idList));
                });

        // 更新用户
        List<NutMap> updateData = memberList.stream().filter(member -> existUseridList.contains(member.getUserId()))
                .map(convert).collect(Collectors.toList());
        AtomicInteger counter = new AtomicInteger(0);
        ListUtil.split(updateData, 300)
                .parallelStream()
                .forEach(list -> {
                    lazyDao.batch().sql(updateSql).dataSet(list).submit();
                    log.error("update {} / {} members", counter.addAndGet(list.size()), updateData.size());
                });

        // 插入用户
        List<NutMap> insertData = memberList.stream().filter(member -> !existUseridList.contains(member.getUserId()))
                .map(convert).collect(Collectors.toList());
        counter.set(0);
        ListUtil.split(insertData, 300)
                .parallelStream()
                .forEach(list -> {
                    lazyDao.batch().sql(insertSql).dataSet(list).submit();
                    log.error("insert {} / {} members", counter.addAndGet(list.size()), insertData.size());
                });
    }

    public void updateUserGenderAndAvatar(String tyOrgId) {
        // 查询需要更新的班级
        List<String> classIdList = lazyDao.selectList(String.class, "select distinct ty_class_id from ty_user " +
                "where ty_org_id = ? and gender is null and ty_class_id is not null", tyOrgId);
        classIdList = classIdList.stream().map(str -> str.split(",")[0]).distinct().collect(Collectors.toList());

        List<TyOrg> tyClassList = lazyDao.selectList(TyOrg.class, "select * from ty_org where id in (?)", classIdList);

        tyClassList.parallelStream().forEach(tyClass -> {
            String tyClassId = tyClass.getId();
            List<TyClassMemberDTO> memberList = client.getClassSMemberList(tyOrgId, tyClassId);     // 教师信息也在里面
            lazyDao.batchUpdate("update ty_user set avatar = :avatar, gender=:gender" +
                    " where ty_user_id = :userId", memberList);
            log.error("更新性别、头像，班级：{}，人数：{}", tyClass.getOrgName(), memberList.size());
        });
//        lazyDao.runSql("update ty_user t0 left join \n" +
//                "t_ty_class t1 on t0.ty_class_id = t1.ty_class_id \n" +
//                "set t0.class_name = t1.class_name\n" +
//                "where t0.class_name is null;");
    }

    public void queryUserAccount(String orgId) {
        List<TyUser> userList = lazyDao.selectList(TyUser.class,
                "select * from ty_user where ty_org_id = ? and account like '%*%' or account is null", orgId);
        if (userList.isEmpty()) {
            log.error("queryUserAccount finished");
            return;
        }

        int size = userList.size();
        AtomicInteger counter = new AtomicInteger();
        userList.parallelStream()
                .forEach(tyUserDO -> {
                    String account = client.getAccountInfo(tyUserDO.getTyUserId());
                    tyUserDO.setAccount(account);
                    lazyDao.runSql("update ty_user set account = ? where id = ?", account, tyUserDO.getId());

                    if (counter.incrementAndGet() % 100 == 0) {
                        log.error("update {} / {}", counter.get(), size);
                    }
                });
        log.error("update {} finished", size);

    }

    public void updateTeacherMobile(String orgId) {
        List<TyUser> userList = lazyDao.selectList(TyUser.class,
                "select * from ty_user where ty_org_id = ? and user_type = 1 and mobile is null", orgId);
        userList.parallelStream()
                .forEach(tyUserDO -> {
                    String mobile = client.getMobile(tyUserDO.getTyUserId());
                    if (StrUtil.isNotBlank(mobile)) {
                        tyUserDO.setMobile(mobile);
                        lazyDao.runSql("update ty_user set mobile = ? where id = ?", mobile, tyUserDO.getId());
                        log.error("teacher {} {}", tyUserDO.getTyUserId(), mobile);
                    }
                });
    }

    public void saveOrgUser(String tyOrgId) {
        Org org = lazyDao.selectOne(Org.class, "select * from org where ty_org_id = ?", tyOrgId);
        Validator.validateNotNull(org, "学校不存在 tyOrgId:{}", tyOrgId);
        String orgId = org.getId();

        // 清除历史数据，仅清除 ty_user 的相关信息
        lazyDao.runSql("DELETE t0 FROM school_has_user t0\n" +
                       "INNER JOIN org_user t1 ON t1.id = t0.user_id\n" +
                       "INNER JOIN ty_user t2 ON t1.ty_user_id = t2.ty_user_id\n" +
                       "WHERE t2.ty_org_id = ?", tyOrgId);
        lazyDao.runSql("DELETE t0 FROM class_has_user t0\n" +
                       "INNER JOIN org_user t1 ON t1.id = t0.user_id\n" +
                       "INNER JOIN ty_user t2 ON t1.ty_user_id = t2.ty_user_id\n" +
                       "WHERE t2.ty_org_id = ?", tyOrgId);

        // all ty_user
        List<TyUser> tyUserList = lazyDao.selectList(TyUser.class, "select * from ty_user where ty_org_id = ?", tyOrgId);

        // 查询 tyOrgId => orgId
        Set<String> tyClassIdSet = new HashSet<>();
        tyUserList.stream().map(TyUser::getTyClassId).filter(Strings::isNotBlank)
                .forEach(str -> {
                    if (str.contains(",")) {
                        tyClassIdSet.addAll(Arrays.asList(str.split(",")));
                    } else {
                        tyClassIdSet.add(str);
                    }
                });
        Map<String, String> tyOrgIdMap = lazyDao.selectList(Org.class,
                        "select id, ty_org_id from org where ty_org_id in (?)", tyClassIdSet)
                .stream().collect(Collectors.toMap(Org::getTyOrgId, Org::getId));

        Date now = new Date();
        List<OrgUser> orgUserList = new LinkedList<>();
        AtomicInteger count = new AtomicInteger(0);
        tyUserList.parallelStream().forEach(tyUser -> {
            String tyUserId = tyUser.getTyUserId();

            // user info
            OrgUser user = lazyDao.selectOne(OrgUser.class, "select * from org_user where ty_user_id = ?", tyUserId);
            if (user == null) {
                user = new OrgUser();
                user.setId(IdWorker.getIdStr());
            }
            user.setUsername(tyUser.getAccount());
            if (Strings.isNotBlank(tyUser.getUserType())) {
                user.setUserType(Integer.parseInt(tyUser.getUserType()));
            }
            user.setRealName(tyUser.getUname());
            user.setMobile(tyUser.getMobile());
            user.setAvatar(tyUser.getAvatar());
            if (Strings.isNotBlank(tyUser.getGender())) {
                user.setGender(Integer.parseInt(tyUser.getGender()));
            }
            user.setTyUserId(tyUserId);
            user.setGmtModified(now);
            user.setEnabled(true);

            // classIdList
            List<String> classIdList = new LinkedList<>();
            if (Strings.isNotBlank(tyUser.getTyClassId())) {
                classIdList.addAll(Arrays.stream(tyUser.getTyClassId().split(","))
                        .map(tyOrgIdMap::get).collect(Collectors.toList()));
            }

            // save org_user
            NutMap columns = CommonMethods.obj2HumpMap(user);
            if (user.getGmtCreate() == null) {
                columns.put("gmt_create", now);
                lazyDao.insert("org_user", columns);
            } else {
                lazyDao.update("org_user", columns, "id=:id");
            }

            saveSchoolHasUser(orgId, user.getId());
            saveClassHasUser(classIdList, user.getId());

            orgUserList.add(user);
            if (count.getAndIncrement() % 200 == 0) {
                log.info("save org user {} / {}", count.get() - 1, tyUserList.size());
            }
        });// end tyUserList forEach
        log.info("save org user {} / {}", count.get(), tyUserList.size());

        // 更新 school_has_user 和 class_has_user 的 user_type
        lazyDao.runSql("update school_has_user t0 left join org_user t1 on t0.user_id = t1.id " +
                "set t0.user_type = t1.user_type where t0.user_type is null");
        lazyDao.runSql("update class_has_user t0 left join org_user t1 on t0.user_id = t1.id " +
                "set t0.user_type = t1.user_type where t0.user_type is null");

        // 更新 class_has_user 的 tree_code
        lazyDao.runSql("update class_has_user t0 left join org t1 on t0.org_id = t1.id\n" +
                "set t0.tree_code = t1.tree_code where t0.tree_code is null;");

        // 更新 org_has_user (校区信息)
        lazyDao.runSql("delete from org_has_user where tree_code like ?", org.getTreeCode() + "%");
        lazyDao.runSql("insert into org_has_user (user_id, user_type, org_id, org_type, tree_code)\n" +
                "select distinct user_id, user_type, t1.id as org_id, org_type, t1.tree_code\n" +
                "from (select distinct user_id, user_type, substr(tree_code, 1, 8) as code from class_has_user where tree_code like ?) t0\n" +
                "         left join org t1 on t0.code = t1.tree_code;", org.getTreeCode() + "%");
    }

    void saveSchoolHasUser(String school, String user) {
        lazyDao.runSql("insert into school_has_user (org_id, user_id) values (?, ?)", school, user);
    }

    void saveClassHasUser(List<String> clazzList, String user) {
        for (String clazz : clazzList) {
            lazyDao.runSql("insert into class_has_user (org_id, user_id) values (?, ?)", clazz, user);
        }
    }
}
